//
// Common include file for unit testing.

#ifndef LONGKEY_TESTING_UNIT_TEST_H_
#define LONGKEY_TESTING_UNIT_TEST_H_

#include <windows.h>
#include <atlstr.h>
#include "../base/scoped_ptr.h"
#include "unittest_debug_helper.h"
#pragma warning(push)
// C4628: digraphs not supported with -Ze.
#pragma warning(disable : 4628)
// C4826: Conversion from 'TYPE *' to 'testing::internal::UInt64' is
// sign-extended. This may cause unexpected runtime behavior.
// Caused by a hack in DefaultPrintTo.
#pragma warning(disable : 4826)
#include "../third_party/gmock/include/gmock/gmock.h"
#pragma warning(pop)
#include "../third_party/gtest/include/gtest/gtest.h"

const TCHAR* const kUnittestName = _T("pcagent_unittest.exe");

// Predicates needed by ASSERT_PRED1 for function returning an HRESULT.
inline testing::AssertionResult Succeeded(const char* s, HRESULT hr) {
	if (SUCCEEDED(hr)) {
		return testing::AssertionSuccess();
	} else {
		CStringA text;
		text.AppendFormat("%s failed with error 0x%08x", s, hr);
		testing::Message msg;
		msg << text;
		return testing::AssertionFailure(msg);
	}
}

inline testing::AssertionResult Failed(const char* s, HRESULT hr) {
	if (FAILED(hr)) {
		return testing::AssertionSuccess();
	} else {
		CStringA text;
		text.AppendFormat("%s failed with error 0x%08x", s, hr);
		testing::Message msg;
		msg << text;
		return testing::AssertionFailure(msg);
	}
}

// Returns true if the variable exists in the environment, even if it is "0".
bool IsEnvironmentVariableSet(const TCHAR* name);

// Returns true if current unit test process owner is LOCALSYSTEM.
bool IsTestRunByLocalSystem();

// Returns the path to the base local app data directory for the user on the
// current OS.
CString GetLocalAppDataPath();

// Returns the path to the base Longkey directory for the user on the current OS.
CString GetLongkeyUserPath();

// Returns the path to the base Longkey PCAgent directory for the user on the
// current OS.
CString GetLongkeyPCAgentUserPath();

// Returns the path to the base Longkey PCAgent directory for the per-machine
// install on the current OS.
CString GetLongkeyPCAgentMachinePath();

// Returns a DWORD registry value from the registry. Assumes the value exists.
// Useful for inline comparisons in EXPECT_EQ.
DWORD GetDwordValue(const CString& full_key_name, const CString& value_name);

// Returns a SZ registry value from the registry. Assumes the value exists.
// Useful for inline comparisons in EXPECT_STREQ.
CString GetSzValue(const CString& full_key_name, const CString& value_name);

// Converts string to GUID. Assumes the string is a valid GUID.
GUID StringToGuid(const CString& str);

const TCHAR* const kRegistryHiveOverrideRoot =
_T("HKCU\\Software\\") _T(SHORT_COMPANY_NAME_ANSI)
_T("\\") _T(PRODUCT_NAME_ANSI)
_T("\\UnitTest\\");
const TCHAR* const kCsidlSystemIdsRegKey =
_T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
const TCHAR* const kCsidlProgramFilesRegValue =
_T("ProgramFilesDir");

// TODO: consider renaming hive_override_key_name to new_key.
// TODO: consider making these utility functions, maybe extend the
//               RegKey class.
//
// Overrides the HKLM and HKCU registry hives so that accesses go to the
// specified registry key instead. The function creates the
// hive_override_key_name. In other words, overriding HKCU with
// "HKCU\\Software\\Longkey\\PCAgent\\UnitTest\\" and accessing HKCU\\Foo results
// in an access at "HKCU\\Software\\Longkey\\PCAgent\\UnitTest\\Foo".
// This method is most often used in SetUp().
void OverrideRegistryHives(const CString& hive_override_key_name);

// Overrides only the specified hives.
// This is useful when modifying registry settings in one hive while using
// code (e.g. WinHttp) that relies on valid registry entries that are difficult
// to reproduce.
//
// TODO: Consider renaming to:
// void OverrideRegistryHive(HKEY hive, const CString& new_key);
void OverrideSpecifiedRegistryHives(const CString& hive_override_key_name,
									bool override_hklm,
									bool override_hkcu);

// Overrides the HKLM and HKCU registry hives so that accesses go to the
// specified registry key instead. Provides permissions to execute local files.
void OverrideRegistryHivesWithExecutionPermissions(
	const CString& hive_override_key_name);

// Restores HKLM and HKCU registry accesses to the real hives.
// This method is most often used in TearDown(). It does not cleanup the
// registry key that is created by OverrideRegistryHives.
void RestoreRegistryHives();

// Specifies the location of psexec.exe. Only call during initialization.
void SetPsexecDir(const CString& dir);

// Returns the location of psexec.exe.
CString GetPsexecDir();

// Accepts the psexec.exe EULA. Only use for automated testing when you have
// already read and agreed to the EULA terms.
// Returns true if the process was successfully started.
bool AcceptPsexecEula();

// Specifies that the tests are running on or on behalf of the build system.
void SetIsBuildSystem();

// Returns whether tests are running on or on behalf of the build system.
bool IsBuildSystem();

// Sets TestSource=buildsystem.
void SetBuildSystemTestSource();

// Returns whether large tests should be run. Large tests are always run on the
// build system and if the "PCAgent_TEST_RUN_LARGE" or "PCAgent_TEST_RUN_ALL"
// environment variable is set.
bool ShouldRunLargeTest();

// Returns whether enormous tests should be run. Enormous tests are always run
// on the build system and if the "PCAgent_TEST_RUN_ALL" environment variable is
// set. This method should be used sparingly and only by tests that take a
// really long time to complete.
bool ShouldRunEnormousTest();

// Terminates all processes named LongkeyPCAgent.exe or LongkeyCrashHandler.exe.
void TerminateAllLongkeyPCAgentProcesses();

// Launches a process and returns its handle.
void LaunchProcess(const CString& exe_path,
				   const CString& args,
				   bool as_system,
				   HANDLE* process);

// Launches a process as system and returns its handle. The function uses
// psexec to run the process.
void LaunchProcessAsSystem(const CString& launch_cmd, HANDLE* process);

// Copies PCAgent installation files under pcagent_path.
void CopyPCAgentFiles(const CString& pcagent_path, const CString& version);

// A generic test fixture that overrides the HKLM and HKCU hives.
class RegistryProtectedTest : public testing::Test
{
protected:
	RegistryProtectedTest()
		: hive_override_key_name_(kRegistryHiveOverrideRoot) {
	}

	virtual void SetUp();
	virtual void TearDown();

	const CString hive_override_key_name_;
};

// Returns the full path of a unique directory under the user temp directory.
CString GetUniqueTempDirectoryName();

// Runs the command as an administrator.
void RunAsAdmin(const CString& exe_path, const CString& cmd_line);

void RegisterOrUnregisterGoopdateLocalServer(bool reg);

void RegisterOrUnregisterGoopdateService(bool reg);

// TODO: Replace custom predicates with EXPECT_HRESULT_SUCCEEDED/FAILED.
#define ASSERT_SUCCEEDED(x) ASSERT_PRED_FORMAT1(PCAgent::Succeeded, x)
#define EXPECT_SUCCEEDED(x) EXPECT_PRED_FORMAT1(PCAgent::Succeeded, x)
#define ASSERT_FAILED(x) ASSERT_PRED_FORMAT1(PCAgent::Failed, x)
#define EXPECT_FAILED(x) EXPECT_PRED_FORMAT1(PCAgent::Failed, x)

// As of Google Test 1.4.0, expressions get converted to 'bool', resulting in
// "warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance
// warning)" in some uses.
// These must be kept in sync with gtest.h.
// TODO: Try to get this fixed in Google Test.
#undef EXPECT_TRUE
#define EXPECT_TRUE(condition) \
	GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
	GTEST_NONFATAL_FAILURE_)
#undef ASSERT_TRUE
#define ASSERT_TRUE(condition) \
	GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
	GTEST_FATAL_FAILURE_)

// GMock's ACTION* macros have 10 parameters, most of which go unused.
// This macro can be used inside ACTION* definitions to suppress warning
// C4100: unreferenced formal parameter.
#define UNREFERENCED_ACTION_PARAMETERS \
	UNREFERENCED_PARAMETER(args); \
	UNREFERENCED_PARAMETER(arg0); \
	UNREFERENCED_PARAMETER(arg1); \
	UNREFERENCED_PARAMETER(arg2); \
	UNREFERENCED_PARAMETER(arg3); \
	UNREFERENCED_PARAMETER(arg4); \
	UNREFERENCED_PARAMETER(arg5); \
	UNREFERENCED_PARAMETER(arg6); \
	UNREFERENCED_PARAMETER(arg7); \
	UNREFERENCED_PARAMETER(arg8); \
	UNREFERENCED_PARAMETER(arg9)

#endif	// LONGKEY_TESTING_UNIT_TEST_H_